package com.charlie.api.client.translator;

import com.charlie.api.client.enums.SupportCodeEnum;
import com.charlie.api.client.model.dto.RegexDTO;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @program: regex-translation
 * @description: 通用翻译器（中文）
 * @author: Charlie
 * @create: 2024-01-16 12:21
 **/
public class CommonTranslator extends AbstractTranslator {


    private static final Map<String, String> PREPROCESS_CHARACTER_MAP;

    private static final Pattern PATTERN_TRANSFERRED = Pattern.compile("\\\\Q(.*?)\\\\E");

    private static final Pattern PATTERN_SPECIAL = Pattern.compile("[.^$*+?(){|\\[\\\\@]");

    static {
        Map<String, String> characterClasses = new HashMap<String, String>();
        characterClasses.put("\\\\d", "[0-9]");
        characterClasses.put("\\\\D", "[^0-9]");
        characterClasses.put("\\\\s", "[ \t\n\f\r]");
        characterClasses.put("\\\\S", "[^ \t\n\f\r]");
        characterClasses.put("\\\\w", "[a-zA-Z_0-9]");
        characterClasses.put("\\\\W", "[^a-zA-Z_0-9]");
        PREPROCESS_CHARACTER_MAP = Collections.unmodifiableMap(characterClasses);
    }

    @Override
    public RegexDTO processRegex(String regex) {
        regex = preProcess(regex);
        RegexDTO regexDTO = new RegexDTO();
        List<RegexDTO.RegexEntry> regexEntries = new ArrayList<>();
        char[] regexArr = regex.toCharArray();
        int len = regexArr.length;
        for (int i = 0; i < len; i++) {
            if (regexArr[i] == SupportCodeEnum.CODELEFT.code || regexArr[i] == SupportCodeEnum.CODESLEFT.code) {
                RegexDTO.RegexEntry entry = new RegexDTO.RegexEntry();
                if (regexArr[i] == SupportCodeEnum.CODESLEFT.code) {
                    entry.setWhole(true);
                }
                int tmp = i + 1;
                while (tmp < len && (regexArr[tmp] != SupportCodeEnum.CODERIGHT.code && regexArr[tmp] != SupportCodeEnum.CODESRIGHT.code)) {
                    tmp++;
                }
                if (regexArr[i + 1] == SupportCodeEnum.NO.code) {
                    entry.setHas(false);
                    i++;
                } else {
                    entry.setHas(true);
                }
                String code = regex.substring(i + 1, tmp);
                ++tmp;
                //{1}省略情况
                entry.setCode(code);
                if (tmp >= len) {
                    entry.setFrom(1);
                    regexEntries.add(entry);
                    break;
                } else if (regexArr[tmp] != SupportCodeEnum.CNTLEFT.code && !SupportCodeEnum.specialCnt(regexArr[tmp])) {
                    entry.setFrom(1);
                    regexEntries.add(entry);
                    i = tmp - 2;
                    continue;
                }
                char c = regexArr[tmp];
                if (c == SupportCodeEnum.ONEMORE.code) {
                    entry.setFrom(1);
                    entry.setTo(-1);
                    tmp++;
                } else if (c == SupportCodeEnum.ZEROMORE.code) {
                    entry.setFrom(0);
                    entry.setTo(-1);
                    tmp++;
                } else if (c == SupportCodeEnum.ZEROONE.code) {
                    entry.setFrom(0);
                    entry.setTo(1);
                    tmp++;
                } else if (c == SupportCodeEnum.CNTLEFT.code) {
                    ++tmp;
                    int end = tmp;
                    while (end < len && regexArr[end] != SupportCodeEnum.CNTRIGHT.code) {
                        end++;
                    }
                    String[] split = regex.substring(tmp, end).split(",");
                    entry.setFrom(Integer.parseInt(split[0]));
                    if (split.length == 2) {
                        entry.setTo(Integer.parseInt(split[1]));
                    } else if (end - tmp > 1) {
                        //{n,}情况
                        entry.setTo(-1);
                    }
                    tmp = end + 1;
                }
                i = tmp - 1;
                regexEntries.add(entry);
            } else if (!SupportCodeEnum.matches(regexArr[i])) {
                RegexDTO.RegexEntry entry = new RegexDTO.RegexEntry();
                entry.setFrom(1);
                entry.setCode(regexArr[i] + "");
                regexEntries.add(entry);
            }
        }
        regexDTO.setRegexEntryList(regexEntries);
        return regexDTO;
    }

    /**
     * 预处理正则
     *
     * @param regex origin
     * @return final regex
     */
    private static String preProcess(String regex) {
        StringBuilder sb = new StringBuilder(regex);
        Matcher matcher = PATTERN_TRANSFERRED.matcher(sb);
        while (matcher.find()) {
            sb.replace(matcher.start(), matcher.end(), PATTERN_SPECIAL.matcher(matcher.group(1)).replaceAll("\\\\$0"));
        }
        String finalRegex = sb.toString();
        for (Map.Entry<String, String> charClass : PREPROCESS_CHARACTER_MAP.entrySet()) {
            finalRegex = finalRegex.replaceAll(charClass.getKey() + "(\\*|\\+|\\?|\\{\\d+(,\\d+)?\\})?", charClass.getValue() + "$1");
        }
        return finalRegex;
    }
}
